---
title: "Object-Oriented Programming"
description: "Learn Kotlin's object-oriented programming features, comparing with JavaScript's class system and object patterns"
---

# Object-Oriented Programming

Welcome to the fifth module of JavaScript to Kotlin conversion! In this module, we'll explore Kotlin's powerful object-oriented programming features and how they provide more robust and type-safe solutions compared to JavaScript's class system and object patterns.

## Learning Objectives

By the end of this module, you will be able to:
- Understand Kotlin's class system and constructor patterns
- Compare Kotlin classes with JavaScript classes
- Implement inheritance and polymorphism effectively
- Use interfaces and abstract classes
- Work with data classes and sealed classes
- Understand object declarations and companion objects
- Apply OOP principles in Kotlin projects

## Understanding Kotlin Classes

### Class Declaration

Kotlin provides a more concise and powerful class system compared to JavaScript. Let's start with basic class declarations.

<UniversalEditor title="Basic Class Declaration" compare={true}>
```javascript !! js
// JavaScript class declaration
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    sayHello() {
        console.log(`Hello, my name is ${this.name}`);
    }
    
    getInfo() {
        return `${this.name}, ${this.age} years old`;
    }
}

// Usage
const person = new Person("Alice", 30);
person.sayHello();
console.log(person.getInfo());
```

```kotlin !! kt
// Kotlin class declaration
class Person(
    val name: String,
    val age: Int
) {
    fun sayHello() {
        println("Hello, my name is $name")
    }
    
    fun getInfo(): String {
        return "$name, $age years old"
    }
}

// Usage
val person = Person("Alice", 30)
person.sayHello()
println(person.getInfo())
```
</UniversalEditor>

### Primary and Secondary Constructors

Kotlin offers more flexible constructor patterns than JavaScript.

<UniversalEditor title="Constructors" compare={true}>
```javascript !! js
// JavaScript constructor patterns
class Employee {
    constructor(name, department, salary = 50000) {
        this.name = name;
        this.department = department;
        this.salary = salary;
        this.id = Math.random().toString(36).substr(2, 9);
    }
    
    // Static method
    static createManager(name, department) {
        return new Employee(name, department, 80000);
    }
}

// Usage
const emp1 = new Employee("Bob", "Engineering");
const manager = Employee.createManager("Carol", "Management");
```

```kotlin !! kt
// Kotlin primary and secondary constructors
class Employee(
    val name: String,
    val department: String,
    val salary: Int = 50000
) {
    val id: String = generateId()
    
    // Secondary constructor
    constructor(name: String, department: String, isManager: Boolean) : 
        this(name, department, if (isManager) 80000 else 50000)
    
    companion object {
        private fun generateId(): String {
            return (0..8).map { ('a'..'z').random() }.joinToString("")
        }
        
        fun createManager(name: String, department: String): Employee {
            return Employee(name, department, true)
        }
    }
}

// Usage
val emp1 = Employee("Bob", "Engineering")
val manager = Employee.createManager("Carol", "Management")
```
</UniversalEditor>

## Inheritance and Polymorphism

### Class Inheritance

Kotlin's inheritance system is more explicit and type-safe than JavaScript's.

<UniversalEditor title="Class Inheritance" compare={true}>
```javascript !! js
// JavaScript inheritance
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    makeSound() {
        console.log("Some sound");
    }
    
    getInfo() {
        return `Animal: ${this.name}`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    
    makeSound() {
        console.log("Woof! Woof!");
    }
    
    getInfo() {
        return `${super.getInfo()}, Breed: ${this.breed}`;
    }
    
    fetch() {
        console.log(`${this.name} is fetching the ball`);
    }
}

// Usage
const dog = new Dog("Buddy", "Golden Retriever");
dog.makeSound();
console.log(dog.getInfo());
dog.fetch();
```

```kotlin !! kt
// Kotlin inheritance
open class Animal(val name: String) {
    open fun makeSound() {
        println("Some sound")
    }
    
    open fun getInfo(): String {
        return "Animal: $name"
    }
}

class Dog(
    name: String,
    val breed: String
) : Animal(name) {
    
    override fun makeSound() {
        println("Woof! Woof!")
    }
    
    override fun getInfo(): String {
        return "${super.getInfo()}, Breed: $breed"
    }
    
    fun fetch() {
        println("$name is fetching the ball")
    }
}

// Usage
val dog = Dog("Buddy", "Golden Retriever")
dog.makeSound()
println(dog.getInfo())
dog.fetch()
```
</UniversalEditor>

### Abstract Classes

Kotlin's abstract classes provide a more structured approach to inheritance.

<UniversalEditor title="Abstract Classes" compare={true}>
```javascript !! js
// JavaScript abstract-like pattern
class Shape {
    constructor(color) {
        if (new.target === Shape) {
            throw new Error("Shape is abstract and cannot be instantiated");
        }
        this.color = color;
    }
    
    getArea() {
        throw new Error("getArea must be implemented");
    }
    
    getPerimeter() {
        throw new Error("getPerimeter must be implemented");
    }
    
    getInfo() {
        return `Shape: ${this.constructor.name}, Color: ${this.color}`;
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    getArea() {
        return Math.PI * this.radius * this.radius;
    }
    
    getPerimeter() {
        return 2 * Math.PI * this.radius;
    }
}

// Usage
const circle = new Circle("red", 5);
console.log(circle.getArea());
console.log(circle.getPerimeter());
```

```kotlin !! kt
// Kotlin abstract classes
abstract class Shape(val color: String) {
    abstract fun getArea(): Double
    abstract fun getPerimeter(): Double
    
    fun getInfo(): String {
        return "Shape: ${this::class.simpleName}, Color: $color"
    }
}

class Circle(color: String, val radius: Double) : Shape(color) {
    override fun getArea(): Double {
        return Math.PI * radius * radius
    }
    
    override fun getPerimeter(): Double {
        return 2 * Math.PI * radius
    }
}

// Usage
val circle = Circle("red", 5.0)
println(circle.getArea())
println(circle.getPerimeter())
```
</UniversalEditor>

## Interfaces

### Interface Implementation

Kotlin's interfaces are more powerful than JavaScript's, supporting default implementations and properties.

<UniversalEditor title="Interfaces" compare={true}>
```javascript !! js
// JavaScript interface-like pattern
class Drawable {
    draw() {
        throw new Error("draw method must be implemented");
    }
}

class Movable {
    move() {
        throw new Error("move method must be implemented");
    }
}

class GameObject extends Drawable {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    draw() {
        console.log(`Drawing at position (${this.x}, ${this.y})`);
    }
    
    move(dx, dy) {
        this.x += dx;
        this.y += dy;
        console.log(`Moved to (${this.x}, ${this.y})`);
    }
}

// Usage
const gameObject = new GameObject(10, 20);
gameObject.draw();
gameObject.move(5, 10);
```

```kotlin !! kt
// Kotlin interfaces
interface Drawable {
    fun draw()
}

interface Movable {
    fun move(dx: Double, dy: Double)
}

class GameObject(
    var x: Double,
    var y: Double
) : Drawable, Movable {
    
    override fun draw() {
        println("Drawing at position ($x, $y)")
    }
    
    override fun move(dx: Double, dy: Double) {
        x += dx
        y += dy
        println("Moved to ($x, $y)")
    }
}

// Usage
val gameObject = GameObject(10.0, 20.0)
gameObject.draw()
gameObject.move(5.0, 10.0)
```
</UniversalEditor>

### Interface with Default Implementation

Kotlin interfaces can have default implementations, unlike JavaScript.

<UniversalEditor title="Interface Default Implementation" compare={true}>
```javascript !! js
// JavaScript mixin pattern
const LoggerMixin = {
    log(message) {
        console.log(`[${this.constructor.name}] ${message}`);
    }
};

class UserService {
    constructor() {
        Object.assign(this, LoggerMixin);
    }
    
    getUser(id) {
        this.log(`Fetching user with id: ${id}`);
        return { id, name: "User" };
    }
}

// Usage
const userService = new UserService();
userService.getUser(123);
```

```kotlin !! kt
// Kotlin interface with default implementation
interface Logger {
    fun log(message: String) {
        println("[${this::class.simpleName}] $message")
    }
}

class UserService : Logger {
    fun getUser(id: Int): User {
        log("Fetching user with id: $id")
        return User(id, "User")
    }
}

data class User(val id: Int, val name: String)

// Usage
val userService = UserService()
userService.getUser(123)
```
</UniversalEditor>

## Data Classes

### Data Classes vs Plain Objects

Kotlin's data classes provide automatic implementations of common methods.

<UniversalEditor title="Data Classes" compare={true}>
```javascript !! js
// JavaScript plain object
const user = {
    id: 1,
    name: "Alice",
    email: "alice@example.com"
};

// Manual implementation of toString, equals, etc.
class User {
    constructor(id, name, email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    toString() {
        return `User(id=${this.id}, name='${this.name}', email='${this.email}')`;
    }
    
    equals(other) {
        if (!(other instanceof User)) return false;
        return this.id === other.id && 
               this.name === other.name && 
               this.email === other.email;
    }
    
    copy(updates = {}) {
        return new User(
            updates.id !== undefined ? updates.id : this.id,
            updates.name !== undefined ? updates.name : this.name,
            updates.email !== undefined ? updates.email : this.email
        );
    }
}

// Usage
const user1 = new User(1, "Alice", "alice@example.com");
const user2 = user1.copy({ name: "Alice Updated" });
console.log(user1.toString());
console.log(user1.equals(user2));
```

```kotlin !! kt
// Kotlin data class
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// Usage
val user1 = User(1, "Alice", "alice@example.com")
val user2 = user1.copy(name = "Alice Updated")
println(user1.toString())
println(user1 == user2)
```
</UniversalEditor>

### Data Class Features

Data classes provide many useful features automatically.

<UniversalEditor title="Data Class Features" compare={true}>
```javascript !! js
// JavaScript manual implementation
class Product {
    constructor(id, name, price, category) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }
    
    // Destructuring
    static destructure(product) {
        const { id, name, price, category } = product;
        return [id, name, price, category];
    }
    
    // Component-wise comparison
    equals(other) {
        return this.id === other.id &&
               this.name === other.name &&
               this.price === other.price &&
               this.category === other.category;
    }
    
    // Hash code
    hashCode() {
        return this.id * 31 + this.name.length;
    }
}

// Usage
const product = new Product(1, "Laptop", 999.99, "Electronics");
const [id, name, price, category] = Product.destructure(product);
console.log(`ID: ${id}, Name: ${name}, Price: ${price}, Category: ${category}`);
```

```kotlin !! kt
// Kotlin data class with automatic features
data class Product(
    val id: Int,
    val name: String,
    val price: Double,
    val category: String
)

// Usage
val product = Product(1, "Laptop", 999.99, "Electronics")
val (id, name, price, category) = product // Destructuring
println("ID: $id, Name: $name, Price: $price, Category: $category")

// Automatic equals, hashCode, toString, copy
val product2 = product.copy(price = 899.99)
println(product == product2)
```
</UniversalEditor>

## Sealed Classes

### Sealed Classes for Type Safety

Kotlin's sealed classes provide compile-time type safety for hierarchies.

<UniversalEditor title="Sealed Classes" compare={true}>
```javascript !! js
// JavaScript union types pattern
class Result {
    static success(data) {
        return new SuccessResult(data);
    }
    
    static error(message) {
        return new ErrorResult(message);
    }
}

class SuccessResult extends Result {
    constructor(data) {
        super();
        this.data = data;
    }
    
    isSuccess() {
        return true;
    }
    
    isError() {
        return false;
    }
}

class ErrorResult extends Result {
    constructor(message) {
        super();
        this.message = message;
    }
    
    isSuccess() {
        return false;
    }
    
    isError() {
        return true;
    }
}

// Usage
function processResult(result) {
    if (result.isSuccess()) {
        console.log("Success:", result.data);
    } else {
        console.log("Error:", result.message);
    }
}

const successResult = Result.success({ id: 1, name: "User" });
const errorResult = Result.error("User not found");
processResult(successResult);
processResult(errorResult);
```

```kotlin !! kt
// Kotlin sealed classes
sealed class Result {
    data class Success(val data: Any) : Result()
    data class Error(val message: String) : Result()
}

// Usage with when expression
fun processResult(result: Result) {
    when (result) {
        is Result.Success -> println("Success: ${result.data}")
        is Result.Error -> println("Error: ${result.message}")
    }
}

val successResult = Result.Success(mapOf("id" to 1, "name" to "User"))
val errorResult = Result.Error("User not found")
processResult(successResult)
processResult(errorResult)
```
</UniversalEditor>

## Object Declarations and Companion Objects

### Singleton Objects

Kotlin's object declarations provide a clean way to implement singletons.

<UniversalEditor title="Singleton Objects" compare={true}>
```javascript !! js
// JavaScript singleton pattern
class DatabaseConnection {
    constructor() {
        if (DatabaseConnection.instance) {
            return DatabaseConnection.instance;
        }
        this.connection = null;
        DatabaseConnection.instance = this;
    }
    
    connect() {
        if (!this.connection) {
            this.connection = { status: "connected" };
            console.log("Database connected");
        }
        return this.connection;
    }
    
    disconnect() {
        if (this.connection) {
            this.connection = null;
            console.log("Database disconnected");
        }
    }
}

// Usage
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true
db1.connect();
db2.disconnect();
```

```kotlin !! kt
// Kotlin object declaration (singleton)
object DatabaseConnection {
    private var connection: Any? = null
    
    fun connect(): Any {
        if (connection == null) {
            connection = mapOf("status" to "connected")
            println("Database connected")
        }
        return connection!!
    }
    
    fun disconnect() {
        if (connection != null) {
            connection = null
            println("Database disconnected")
        }
    }
}

// Usage
val db1 = DatabaseConnection
val db2 = DatabaseConnection
println(db1 === db2) // true
db1.connect()
db2.disconnect()
```
</UniversalEditor>

### Companion Objects

Companion objects provide static-like functionality in Kotlin.

<UniversalEditor title="Companion Objects" compare={true}>
```javascript !! js
// JavaScript static methods
class MathUtils {
    static PI = 3.14159;
    
    static add(a, b) {
        return a + b;
    }
    
    static multiply(a, b) {
        return a * b;
    }
    
    static createRandom() {
        return Math.random();
    }
}

// Usage
console.log(MathUtils.PI);
console.log(MathUtils.add(5, 3));
console.log(MathUtils.multiply(4, 7));
console.log(MathUtils.createRandom());
```

```kotlin !! kt
// Kotlin companion object
class MathUtils {
    companion object {
        const val PI = 3.14159
        
        fun add(a: Double, b: Double): Double {
            return a + b
        }
        
        fun multiply(a: Double, b: Double): Double {
            return a * b
        }
        
        fun createRandom(): Double {
            return Math.random()
        }
    }
}

// Usage
println(MathUtils.PI)
println(MathUtils.add(5.0, 3.0))
println(MathUtils.multiply(4.0, 7.0))
println(MathUtils.createRandom())
```
</UniversalEditor>

## Advanced OOP Patterns

### Extension Functions

Kotlin's extension functions allow adding functionality to existing classes.

<UniversalEditor title="Extension Functions" compare={true}>
```javascript !! js
// JavaScript prototype extension
String.prototype.reverse = function() {
    return this.split('').reverse().join('');
};

String.prototype.capitalize = function() {
    return this.charAt(0).toUpperCase() + this.slice(1);
};

// Usage
const text = "hello world";
console.log(text.reverse());
console.log(text.capitalize());
```

```kotlin !! kt
// Kotlin extension functions
fun String.reverse(): String {
    return this.reversed()
}

fun String.capitalize(): String {
    return this.replaceFirstChar { it.uppercase() }
}

// Usage
val text = "hello world"
println(text.reverse())
println(text.capitalize())
```
</UniversalEditor>

### Operator Overloading

Kotlin allows operator overloading for custom types.

<UniversalEditor title="Operator Overloading" compare={true}>
```javascript !! js
// JavaScript operator overloading simulation
class Vector {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    add(other) {
        return new Vector(this.x + other.x, this.y + other.y);
    }
    
    multiply(scalar) {
        return new Vector(this.x * scalar, this.y * scalar);
    }
    
    toString() {
        return `Vector(${this.x}, ${this.y})`;
    }
}

// Usage
const v1 = new Vector(1, 2);
const v2 = new Vector(3, 4);
const result = v1.add(v2);
const scaled = v1.multiply(2);
console.log(result.toString());
console.log(scaled.toString());
```

```kotlin !! kt
// Kotlin operator overloading
data class Vector(val x: Double, val y: Double) {
    operator fun plus(other: Vector): Vector {
        return Vector(x + other.x, y + other.y)
    }
    
    operator fun times(scalar: Double): Vector {
        return Vector(x * scalar, y * scalar)
    }
}

// Usage
val v1 = Vector(1.0, 2.0)
val v2 = Vector(3.0, 4.0)
val result = v1 + v2
val scaled = v1 * 2.0
println(result)
println(scaled)
```
</UniversalEditor>

## Practical Examples

### Building a Simple Banking System

Let's create a practical example that demonstrates Kotlin's OOP features.

<UniversalEditor title="Banking System Example" compare={true}>
```javascript !! js
// JavaScript banking system
class Account {
    constructor(accountNumber, balance = 0) {
        this.accountNumber = accountNumber;
        this.balance = balance;
        this.transactions = [];
    }
    
    deposit(amount) {
        if (amount > 0) {
            this.balance += amount;
            this.transactions.push({
                type: 'deposit',
                amount: amount,
                timestamp: new Date()
            });
            return true;
        }
        return false;
    }
    
    withdraw(amount) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
            this.transactions.push({
                type: 'withdraw',
                amount: amount,
                timestamp: new Date()
            });
            return true;
        }
        return false;
    }
    
    getBalance() {
        return this.balance;
    }
    
    getTransactionHistory() {
        return [...this.transactions];
    }
}

class SavingsAccount extends Account {
    constructor(accountNumber, balance = 0, interestRate = 0.02) {
        super(accountNumber, balance);
        this.interestRate = interestRate;
    }
    
    addInterest() {
        const interest = this.balance * this.interestRate;
        this.deposit(interest);
        return interest;
    }
}

// Usage
const account = new SavingsAccount("12345", 1000, 0.05);
account.deposit(500);
account.withdraw(200);
account.addInterest();
console.log("Balance:", account.getBalance());
console.log("Transactions:", account.getTransactionHistory());
```

```kotlin !! kt
// Kotlin banking system
sealed class Transaction {
    data class Deposit(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
    data class Withdraw(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
    data class Interest(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
}

open class Account(
    val accountNumber: String,
    protected var balance: Double = 0.0
) {
    private val transactions = mutableListOf<Transaction>()
    
    fun deposit(amount: Double): Boolean {
        return if (amount > 0) {
            balance += amount
            transactions.add(Transaction.Deposit(amount))
            true
        } else false
    }
    
    fun withdraw(amount: Double): Boolean {
        return if (amount > 0 && amount <= balance) {
            balance -= amount
            transactions.add(Transaction.Withdraw(amount))
            true
        } else false
    }
    
    fun getBalance(): Double = balance
    
    fun getTransactionHistory(): List<Transaction> = transactions.toList()
}

class SavingsAccount(
    accountNumber: String,
    balance: Double = 0.0,
    private val interestRate: Double = 0.02
) : Account(accountNumber, balance) {
    
    fun addInterest(): Double {
        val interest = balance * interestRate
        deposit(interest)
        return interest
    }
}

// Usage
val account = SavingsAccount("12345", 1000.0, 0.05)
account.deposit(500.0)
account.withdraw(200.0)
account.addInterest()
println("Balance: ${account.getBalance()}")
println("Transactions: ${account.getTransactionHistory()}")
```
</UniversalEditor>

## Best Practices and Patterns

### When to Use Different Class Types

<UniversalEditor title="Class Type Guidelines" compare={true}>
```javascript !! js
// JavaScript class usage guidelines
// 1. Regular classes for complex objects with behavior
class UserService {
    constructor() {
        this.users = new Map();
    }
    
    addUser(user) {
        this.users.set(user.id, user);
    }
    
    getUser(id) {
        return this.users.get(id);
    }
}

// 2. Plain objects for simple data structures
const userData = {
    id: 1,
    name: "Alice",
    email: "alice@example.com"
};

// 3. Factory functions for object creation
function createUser(id, name, email) {
    return {
        id,
        name,
        email,
        toString() {
            return `User(id=${this.id}, name='${this.name}')`;
        }
    };
}
```

```kotlin !! kt
// Kotlin class usage guidelines
// 1. Regular classes for complex objects with behavior
class UserService {
    private val users = mutableMapOf<Int, User>()
    
    fun addUser(user: User) {
        users[user.id] = user
    }
    
    fun getUser(id: Int): User? {
        return users[id]
    }
}

// 2. Data classes for simple data structures
data class UserData(
    val id: Int,
    val name: String,
    val email: String
)

// 3. Object declarations for singletons
object UserFactory {
    fun createUser(id: Int, name: String, email: String): UserData {
        return UserData(id, name, email)
    }
}
```
</UniversalEditor>

## Summary

In this module, we've explored Kotlin's powerful object-oriented programming features:

### Key Takeaways

1. **Class Declaration**: Kotlin's primary constructor syntax is more concise than JavaScript's class constructors
2. **Inheritance**: Kotlin uses `open` keyword and explicit inheritance with `:`
3. **Interfaces**: Kotlin interfaces support default implementations and properties
4. **Data Classes**: Automatic generation of `equals()`, `hashCode()`, `toString()`, and `copy()`
5. **Sealed Classes**: Compile-time type safety for class hierarchies
6. **Object Declarations**: Clean singleton implementation
7. **Companion Objects**: Static-like functionality within classes
8. **Extension Functions**: Adding functionality to existing classes
9. **Operator Overloading**: Custom operators for user-defined types

### Comparison with JavaScript

| Feature | JavaScript | Kotlin |
|---------|------------|--------|
| Class Declaration | `class` with constructor | Primary constructor syntax |
| Inheritance | `extends` | `:` with `open` keyword |
| Interfaces | No native support | Full interface support |
| Data Classes | Manual implementation | Automatic with `data class` |
| Singletons | Manual pattern | `object` declaration |
| Static Methods | `static` keyword | `companion object` |
| Extension Methods | Prototype modification | Extension functions |

### Next Steps

In the next module, we'll explore Android development with Kotlin, including:
- Android project structure
- Activity and Fragment lifecycle
- Layout and UI components
- Event handling and data binding
- Modern Android development with Jetpack Compose

This foundation in Kotlin's OOP features will be essential for building robust Android applications and understanding modern Kotlin development patterns. 